package ldh.im.fxbase.service;

import ldh.im.dto.business.item.FileItem;
import ldh.im.dto.business.message.MessageObject;
import ldh.im.dto.business.message.MessageUserSendInfo;
import ldh.im.fxbase.dao.*;
import ldh.im.fxbase.data.dto.RefMessageObject;
import ldh.im.fxbase.data.pojo.*;
import ldh.im.fxbase.data.pojo.MessageExt;
import ldh.im.fxbase.data.pojo.status.*;
import ldh.im.fxbase.util.*;
import ldh.im.util.*;
import ldh.rpc.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

public class SaveMessageService {

    private static final Logger LOGGER = LoggerFactory.getLogger(SaveMessageService.class);

    private ImMessageDao imMessageDao = BeanFactory.getInstance().getBean(ImMessageDao.class);
    private ImMessageSendDao imMessageSendDao = BeanFactory.getInstance().getBean(ImMessageSendDao.class);
    private ImGroupDao imGroupDao = BeanFactory.getInstance().getBean(ImGroupDao.class);
    private ImSessionDao imSessionDao = BeanFactory.getInstance().getBean(ImSessionDao.class);
    private ImFileDao imFileDao = BeanFactory.getInstance().getBean(ImFileDao.class);

    /**
     * 消息持久化处理：
     * 1，保存消息（包含引用消息）
     * 2，消息撤回或已更新的消息进行变更
     * 3，消息的删除
     * @param messageObjectList
     * @return
     * @throws SQLException
     */
    public List<ImMessage> saveMessages(String userId, List<MessageObject> messageObjectList, boolean isSynMessage) throws SQLException {
        Set<ImMessage> insertImMessages = new LinkedHashSet<>(messageObjectList.size());
        Set<ImMessage> updateMessages = new LinkedHashSet<>();
        Set<ImMessage> deleteMessages = new LinkedHashSet<>();
        Set<ImSession> updateSessions = new LinkedHashSet<>();

        for(MessageObject messageObject : messageObjectList) {
            ImMessage imMessage = null;
            if (!(messageObject instanceof RefMessageObject)) { // 非引用消息
                ImSession imSession = getOrBuildImSession(userId, messageObject);
                if (imSession == null) {
                    imSession = buildMessageSession(userId, messageObject);
                    imSessionDao.insertSession(imSession);
                } else {
                    String latelyMessage = MessageUtil.buildLastedMessage(messageObject.getMessageType(), messageObject.getContent());
                    imSession.setLatelyMessage(latelyMessage);

                    imSession.setUpdateTime(new Date());
                    updateSessions.add(imSession);
                }
                imMessage = toImMessage(userId, imSession, messageObject);
            } else { // 引用消息的会话id为空，并在前端单独展示
                imMessage = toImMessage(userId, null, messageObject);
            }

            if (imMessage == null) continue;
            ImMessage dbMessage = imMessageDao.getMessageByServerId(userId, imMessage.getServerId());
            if (dbMessage != null) {
                if (imMessage.getStatus() == ImMessageStatus.withdraw || imMessage.getStatus() == ImMessageStatus.updated) {
                    updateMessages.add(imMessage);
                } else if (imMessage.getStatus() == ImMessageStatus.delete) {
                    deleteMessages.add(imMessage);
                } else if (imMessage.getStatus() == ImMessageStatus.ref) {
                    continue;
                } else {
                    LOGGER.error("客户端不应该收到这个消息，messageId: {}, currentUserId: {}", imMessage.getServerId(), userId);
                }
            } else {
                insertImMessages.add(imMessage);
            }
        }
        imMessageDao.saveMessages(insertImMessages.stream().collect(Collectors.toList()));
        imMessageSendDao.batchInsertMessageSends(insertImMessages.stream().collect(Collectors.toList()));

        imMessageDao.batchUpdateMessages(updateMessages.stream().collect(Collectors.toList()));
        imMessageDao.batchDeleteMessages(deleteMessages.stream().collect(Collectors.toList()));

        // 变更最新消息
        imSessionDao.batchUpdates(updateSessions.stream().collect(Collectors.toList()));

        initRefMessags(insertImMessages);

        if (isSynMessage) { // 是否为消息同步，如果是消息同步，需要进行热点数据更新
            // 计算未读消息数
            Map<Long, List<ImMessage>> sessionMessages = insertImMessages.stream().filter(m->m.getSessionId() != null).collect(Collectors.groupingBy(m->m.getSessionId()));

            for(Map.Entry<Long, List<ImMessage>> entry : sessionMessages.entrySet()) {
                Long hotMessageTotal = entry.getValue().stream().filter(m->!m.isSender(userId)).filter(m->m.getActionStatus() == ImMessageActionStatus.none).count();
                if (hotMessageTotal > 0) {
                    imSessionDao.incrHotMessageNum(entry.getKey(), hotMessageTotal.intValue());
                }
            }
        }

        return insertImMessages.stream().collect(Collectors.toList());
    }

    /**
     * 将接受到的消息转化成客户端消息
     * @param imSession
     * @param message
     * @return
     * @throws SQLException
     */
    private ImMessage toImMessage(String userId, ImSession imSession, MessageObject message) throws SQLException {
        if (message.getFromUserId() == null) {
            throw new RuntimeException("fromUserId is null" + JsonUtil.toJson(message));
        }
        ImMessage imMessage = new ImMessage();
        imMessage.setCurrentUserId(userId);
        if (message.getLocalSeq().contains("m-")) {
            String serverId = message.getLocalSeq().split("m-")[0];
            imMessage.setServerId(serverId);
        } else {
            imMessage.setServerId(message.getLocalSeq());
        }
        imMessage.setClientId(message.getLocalSeq());
        imMessage.setFromUserId(message.getFromUserId());
        imMessage.setToUserId(message.getToUserId());
        imMessage.setGroupCode(message.getGroupCode());

        Set<MessageUserSendInfo> messageUserSendInfos = message.getUserSendInfoSet();
        if (messageUserSendInfos != null) {
            imMessage.setMessageSendSet(messageUserSendInfos.stream().map(ui->buildMessageSend(imMessage, ui, imMessage.isSender(userId))).collect(Collectors.toSet()));
        }

        buildMessageFileStatus(imMessage);

        if (message instanceof RefMessageObject) {
            RefMessageObject refMessageObject = (RefMessageObject) message;
            if (refMessageObject.isRefRoot()) {
                imMessage.setStatus(ImMessageStatus.ref);
            }
        } else {
            imMessage.setStatus(ImMessageStatus.wrap(message.getStatus(), imMessage.isSender(userId)));
        }

        imMessage.setVersion(1L);
        imMessage.setMessageType(message.getMessageType());
        imMessage.setServerReceiveTime(DateUtil.toDate(message.getCreateDateTime()));
        imMessage.setContent(message.getContent());
        handleBytes(userId, imMessage, message);
        imMessage.setActionStatus(ImMessageActionStatus.wrap(message.getActionStatus(), imMessage.isSender(userId)));
        if (imMessage.getMessageType().isFile() && imMessage.getFileStatus() == ImMessageFileStatus.none) {
            imMessage.setFileStatus(ImMessageFileStatus.uploading);
        }

        if (message.isRef()) {
            imMessage.setRefType(ImMessageRefType.wrap(message.getRefType()));
            imMessage.setRefMessageIds(message.getRefMessageIds());
        }
        if (imSession != null) {
            imMessage.setSessionId(imSession.getId());
            imMessage.setImSession(imSession);
        } else {
            imMessage.setStatus(ImMessageStatus.ref);
        }
        imMessage.setCreateDateTime(DateUtil.toDate(message.getCreateDateTime()));
        imMessage.setUpdateDateTime(new Date());
        return imMessage;
    }

    // 获取消息的会话
    private ImSession getOrBuildImSession(String userId, MessageObject message) throws SQLException {
        ImSession imSession = null;
        if (message.isGroup()) {
            ImGroup imGroup = imGroupDao.getByUserIdAndId(userId, message.getGroupCode());
            if (imGroup == null) {
                ImGroupService groupService = new ImGroupService();
                imGroup = groupService.pullGroup(userId, message.getGroupCode());
            }
            imSession = imSessionDao.getByGroupCodeAndUserId(imGroup.getServerId(), userId);
        } else {
            imSession = imSessionDao.getByUserId(userId, message.getFromUserId(), message.getToUserId());
        }
        return imSession;
    }

    // 处理消息附件
    private void handleBytes(String userId, ImMessage imMessage, MessageObject message) {
        if(message.getMessageType().isFile()) {
            FileItem fileItem = (FileItem) message.getBean();
            MessageExt messageExt = new MessageExt();
            if (fileItem == null) {
                try {
                    fileItem = JsonUtil.toObject(message.getContent(), FileItem.class);
                    message.setBean(fileItem);
                    String fileName = fileItem.getFileName();
                    messageExt.setFileName(fileName);
                    messageExt.setOriginalName(fileItem.getOriginalName());
                    messageExt.setFileUrl(fileItem.getFileUrl());
                    messageExt.setMd5(fileItem.getMd5());
                    messageExt.setSize(fileItem.getSize());
                    messageExt.setTime(fileItem.getTime());
                    messageExt.setImageWidth(fileItem.getImageWidth());
                    messageExt.setImageHeight(fileItem.getImageHeight());
                    imMessage.setContent(fileItem.getFileUrl());
                } catch (Exception e) {
                    LOGGER.error("解码FileItem失败, message : {}", message.getContent());
                }

            }
            messageExt.setRemote(true);
            imMessage.setMessageExt(messageExt);
            imMessage.initExt();
        }
    }

    /**
     * 够建会话
     * @param userId
     * @param messageObject
     * @return
     */
    private ImSession buildMessageSession(String userId, MessageObject messageObject) {
        String toUserId = messageObject.getToUserId();
        ImSession imSession = null;
        if (messageObject.isP2p()) {
            if (userId.equals(toUserId)) {
                toUserId = messageObject.getFromUserId();
            }
            ImUser toUser = ImCacheFactory.getInstance().getUser(toUserId);
            imSession = ImSession.build(userId, toUser);
        } else {
            ImGroup imGroup = ImCacheFactory.getInstance().getImGroup(userId, messageObject.getGroupCode());
            imSession = ImSession.build(userId, imGroup);
            imSession.setGroupCode(messageObject.getGroupCode());
            imSession.setName(ImCacheFactory.getInstance().getImGroup(userId, messageObject.getGroupCode()).getName());
        }

        imSession.setServerId(messageObject.getServerId());
        imSession.setStatus(ImSessionStatus.enable);
        String latelyMessage = MessageUtil.buildLastedMessage(messageObject.getMessageType(), messageObject.getContent());
        imSession.setLatelyMessage(latelyMessage);
        return imSession;
    }

    private ImMessageSend buildMessageSend(ImMessage imMessage, MessageUserSendInfo ui, boolean isSender) {
        ImMessageSend messageSend = new ImMessageSend();
        messageSend.setUserId(ui.getUserId());
        messageSend.setActionStatus(ImMessageActionStatus.wrap(ui.getActionStatus(), isSender));
        messageSend.setFileStatus(ImMessageFileStatus.wrap(ui.getFileStatus(), isSender));
        messageSend.setStatus(ImMessageStatus.wrap(ui.getStatus(), imMessage.isSender(ui.getUserId())));
        if (messageSend.getUserId().equals(imMessage.getFromUserId())) {
            messageSend.setActionStatus(ImMessageActionStatus.readed);
        }
        return messageSend;
    }

    private void initRefMessags(Set<ImMessage> messages) throws SQLException {
        for (ImMessage message : messages) {
            if (message.getStatus() == ImMessageStatus.ref) continue;
            if (Objects.isNull(message.getRefMessageIds())) continue;
            String[] refMessageIds = message.getRefMessageIds().split(",");
            List<ImMessage> refMessages = this.imMessageDao.getByServerIdsAndUserId(Arrays.stream(refMessageIds).collect(Collectors.toSet()), message.getCurrentUserId());
            message.setRefMessages(refMessages);
        }
    }

    private void buildMessageFileStatus(ImMessage imMessage) throws SQLException {
        String userId = imMessage.getCurrentUserId();
        if (imMessage.isAttachFile()) {
            if (FileUtil.downFileExisted(imMessage)) {
                ImMessageFileStatus fileStatusTmp = imMessage.isSender(userId) ? ImMessageFileStatus.downloaded : ImMessageFileStatus.downloaded;
                imMessage.setFileStatus(fileStatusTmp);
            } else {
                imMessage.initMessageExt();
                ImFile imFile = imFileDao.getBySign(userId, imMessage.getMessageExt().getMd5());
                if (imFile != null) {
                    imMessage.getMessageExt().setFileUrl(imFile.getPath());
                    imMessage.initExt();
                }
            }
        }
    }
}
